/**
 * Title: Hyperbolic engine
 * Description: jFamilyTree Engine
 * Version: provider
 * Copyright: (c) 2001-2008
 * Terms of use:see license agreement at http://treebolic.sourceforge.net/en/license.htm
 * Author: Bernard Bou
 * Company: bsys
 * Update: Mon Mar 10 00:00:00 CEST 2008
 */
package jFamilyTree.model;

import java.util.List;

import jFamilyTree.core.Complex;
import jFamilyTree.core.EuclidianLocation;

/**
 * Model
 * 
 * @author Bernard Bou
 */
public class Tree
{
	// D A T A

	/**
	 * Root node
	 */
	private INode theRoot;

	/**
	 * Edge list
	 */
	private List<IEdge> theEdges;

	// C O N S T R U C T O R

	/**
	 * Constructor
	 * 
	 * @param thisRoot
	 *        root node
	 * @param theseEdges
	 *        edge list
	 */
	public Tree(INode thisRoot, List<IEdge> theseEdges)
	{
		theRoot = thisRoot;
		theEdges = theseEdges;
	}

	// A C C E S S

	/**
	 * Get root node
	 * 
	 * @return root node
	 */
	public INode getRoot()
	{
		return theRoot;
	}

	/**
	 * Get edges
	 * 
	 * @return edges
	 */
	public List<IEdge> getEdges()
	{
		return theEdges;
	}

	/**
	 * Set root node
	 * 
	 * @param thisRoot
	 *        node
	 */
	public void setRoot(INode thisRoot)
	{
		theRoot = thisRoot;
	}

	/**
	 * Set edges
	 * 
	 * @param theseEdges
	 *        edge list
	 */
	public void setEdges(List<IEdge> theseEdges)
	{
		theEdges = theseEdges;
	}

	// S E A R C H I N G

	/**
	 * Find node nearest to location
	 * 
	 * @param thisStart
	 *        start node (only descendants are considered)
	 * @param thisLocation
	 *        unit circle location
	 * @return node if found, null otherwise
	 */
	public INode findNodeAt(INode thisStart, Complex thisLocation)
	{
		if (thisStart == null)
			return null;

		INode thisResult = thisStart;
		Location thisResultLocation = thisStart.getLocation();

		// find nearest (using squares as a measure)
		double thisDistance = EuclidianLocation.getDistanceSquare(thisResultLocation.hyper.center, thisLocation);
		List<INode> theseChildren = thisStart.getChildren();
		if (theseChildren != null)
			for (INode thisChild : theseChildren)
			{
				INode thisTargetNode = findNodeAt(thisChild, thisLocation);
				if (thisTargetNode != null)
				{
					Location thisTargetLocation = thisTargetNode.getLocation();
					if (!thisTargetLocation.hyper.isBorder)
					{
						double thatDistance = EuclidianLocation.getDistanceSquare(thisTargetLocation.hyper.center, thisLocation);
						if (thatDistance < thisDistance)
						{
							thisResult = thisTargetNode;
							thisResultLocation = thisTargetLocation;
							thisDistance = thatDistance;
						}
					}
				}
			}

		if (thisResult != null && (thisResultLocation.euclidian.radius * thisResultLocation.euclidian.radius) <= thisDistance)
			return null;
		return thisResult;
	}

	/**
	 * Find node by id
	 * 
	 * @param thisStart
	 *        start node
	 * @param thisId
	 *        target id
	 * @return node if found, null otherwise
	 */
	public INode findNodeById(INode thisStart, String thisId)
	{
		if (thisStart == null)
			return null;

		// node test
		if (thisId.equals(thisStart.getId()))
			return thisStart;

		// children
		for (INode thisChildNode : thisStart.getChildren())
		{
			INode thisNode = findNodeById(thisChildNode, thisId);
			if (thisNode != null)
				return thisNode;
		}
		return null;
	}
}
